

let a={};
//消息中心
function MessageCbk(obj:any)
{
    console.log(`changed:${JSON.stringify(obj)}`);
}
function Changed(obj:any){
    //发出更改消息并冒泡
    //发出此对象更改消息
    MessageCbk(obj);
    //发出parent更改消息
    if(obj[Parent]!=null) Changed(obj[Parent]);
}
///
const Parent=Symbol("父对象的代理对象的指针");
const SelfName=Symbol("此对象在父对象中的属性名，如果有")
const NoProxy=Symbol("表明此对象不需要Proxy化");
const NoEvent=Symbol("表明此对象不监听更改事件");
function printChain(obj:any)
{
    //获得parent对象并打印parent对象的信息
    let parent=obj[Parent];
    if(parent!=null) printChain(parent);
    //打印自己的信息
    console.log(obj);
}
export type WatchHandler=(pars:ChangeObject)=>void;
function setObject(obj:Object,key:PropertyKey,value:any,noevent=false,noproxy=false)
{
    let nobj=value;
    obj[NoEvent]=noevent;
    if(typeof value=="object") nobj[NoProxy]=noproxy;
    obj[key]=nobj;
    delete obj[NoEvent];
    if(typeof value=="object") delete nobj[NoProxy];
}
function getPathPtr(obj:any,path:string,ensure:boolean=false,noevent=false,noproxy=false)
{
    let arr=path.split("/").slice(1);
    let npth=arr.slice(0,-1);
    let prop=arr[arr.length-1];
    let now=obj;
    for(let p of npth){
        if(now[p]==null) {
            if(ensure) {
                setObject(now,p,{},noevent,noproxy);
            }
            else throw "错误，路径不存在";
        }
        now=now[p];
    }
    return {obj:now,prop};
}
//确认存在
export let ensuerPathExists=(obj:any,path:string)=>{getPathPtr(obj,path);}
//根据path访问对象
export function setProp(obj:any,path:string,value:any,force:boolean=false,noevent=false,noproxy=false)
{
    let res=getPathPtr(obj,path,force,noevent,noproxy);
    setObject(res.obj,res.prop,value,noevent,noproxy);
    //返回的是有Proxy的对象或值
    return res.obj[res.prop];
}
export function getProp(obj:any,path:string)
{
    if(path=="/") return obj;
    let res=getPathPtr(obj,path);
    return res.obj[res.prop];
}
export function getTopAndPath(obj:any)
{
    let nowpath="";
    let now=obj;
    while(now[Parent]!=null)
    {
        nowpath=`/${now[SelfName]}${nowpath}`;
        now=now[Parent];
    }
    if(nowpath=="") nowpath="/";
    return {top:now,nowpath};
}
/**
 * 获取一个object自带的路径
 * 追溯Parent获取obj相对顶层对象的路径
 * @param obj 要获取路径的对象
 */
export function getPath(obj:any)
{
    return getTopAndPath(obj).nowpath;   
}

///数据结构声明
export interface ChangeInfo
{
    old:any;
    new:any;
}
interface ChangeObject
{
    obj:any;
    target:ChangeInfo;
    targetPath:string
}

export interface IWatcher
{
    toProxiedData<T>(obj:T,parent:any,key:PropertyKey):T;
}
export interface IHandle
{
    Changed(pars:ChangeObject):any;
}
/**
 * 使用方法 let obj=new Watcher(handler).toProxiedData(obj);
 */
export class Watcher
{
    //指示是否暂停发出Change事件
    public isListen=true;
    //target中的old和new 都是Proxied对象或值
    protected Changed(obj:any,target:ChangeInfo,targetPath:string)
    {
        this.Handler({obj,target,targetPath});
        //如果冒泡且存在父对象才继续递归
        if(this.isBubble&&obj[Parent]!=null) this.Changed(obj[Parent],target,`/${String(obj[SelfName])}${targetPath}`);
    }
    /**
     * @param Handler 通知函数
     * @param isBubble 是否采用事件冒泡（即下层对象修改后，上层对象会接到通知)
     */
    constructor(public Handler:WatchHandler,public isBubble:boolean=true){
        let _this=this;
        let pobj={
            set(target: any, p: PropertyKey, value: any, receiver: any):boolean{
                //只要有属性更改就加个代理
                if(p==NoProxy&&p==NoEvent) {
                    target[p]=value;
                    return true;
                }
                const desc=Object.getOwnPropertyDescriptor(target,p);
                if(p in target &&(desc==null||desc.writable==false))
                {
                    //不代理只读对象
                    return true;
                }
                let old=target[p];
                if(typeof value =='object'&&value[NoProxy]!=true)
                {
                    //如果此处在修改一个对象的parent，则会导致parent对象被加上Proxy
                    //这使得Parent对象本身的更改也会被此对象监听(重复设置Proxy)
                    //同时下面的代码设置Parent的Parent指针指向此对象导致“循环指向” 出错
                    //因此Parent对象是只读且不可修改的
                    let childProxy=new Proxy(value,pobj);
                    target[p]=childProxy;
                    //在目标对象上记录下parent指针 parent可以为父对象的proxy或直接为对象本身
                    //在value上记录parent则parent的更改不被监视  否则将形成无限递归
                    //即在子对象上记录parent时等同于给子对象加了一个属性
                    //这个属性又需要以子对象为parent 以此类推
                    //故parent不通过代理
                    //value为receiver表示parent指向的是Parent的Proxy对象,因此对其parent的操作
                    //也会被监视
                    Object.defineProperty(value,Parent,{
                        value:receiver,
                        enumerable:false,
                        writable:false,
                        configurable:false
                    });
                    //记录此对象在父对象中的属性名
                    Object.defineProperty(value,SelfName,{
                        value:p,
                        enumerable:false,
                        writable:false,
                        configurable:false
                    });
                }
                else
                {
                    target[p]=value;
                }
                if(_this.isListen&&target[NoEvent]!=true){
                    // printChain(target);
                    //通知
                    _this.Changed(receiver,{
                        old:old,
                        new:target[p]},`/${String(p)}`);
                }
                return true;
            }
        } as ProxyHandler<any>;
        this.ProxyObject=pobj;
    }
    protected ProxyObject=null as unknown as ProxyHandler<any>;
    /**
     * 应该遵循自顶向下逐层赋值的原则
     * parent已经是Proxy对象或直接为null
     * 转换原始对象 直接在原始对象上转换
     * @param obj 要转换的对象
     * @param parent 父对象（将被赋值给父对象）可以为Proxy化的对象
     * @param key 此对象在父对象中的属性名
     */
    public toProxiedData<T>(obj:T,parent:any=null,key:PropertyKey=""):T{
        try{
            let ret=null as unknown as T;
            //如果父对象不存在 则ret直接为obj的代理
            obj[NoEvent]=true;
            if(parent==null) ret=new Proxy(obj,this.ProxyObject);
            else
            {
                //赋值给父对象
                parent[key]=obj;
                //从父对象中得到处理过的子对象
                ret=parent[key];
            }
            //处理下级对象 转换所有object
            for(let key in obj)
            {
                if(obj[key]!=null&&typeof obj[key]=="object")
                    this.toProxiedData(obj[key],ret,key);
            }
            //为避免触发读取事件 这里直接操作原始对象
            delete obj[NoEvent];
            return ret;
        }catch(e)
        {
            return obj;
        }
    }


    
}
/**
 * 写入器 接受更改信息 对监控对象进行修改
 */
export class Writer
{

}
/**
 * 代理Watch Changed事件由外部触发
 */
export class ProxyWatcher extends Watcher
{
    //把监视对象代理到rawwatcher上
    public toProxiedData<T>(obj: T, parent: any, key: string | number | symbol): T {
        return this.rawwatcher.toProxiedData(obj,parent,key);
    }
    constructor(public rawwatcher:Watcher,public middleFunc:WatchHandler)
    {
        super(rawwatcher.Handler);
        //把原始watcher的change事件转发到这个函数
        rawwatcher.Handler=(pars)=>{
            middleFunc(pars);
            this.Handler(pars);
        }
    }
}


/**
 * 带前缀的Watch 在会给path加前缀
 */
export class PrefixWatcher extends Watcher
{

    constructor(handler:WatchHandler,public readonly prefix:string,isBubble:boolean=true){
        super(({obj,target,targetPath})=>{
            let targetpath=targetPath;
            //这里需要特殊处理 如果prefix为空则不处理
            if(targetpath=="/") targetpath=prefix==""? targetpath:prefix;
            else targetpath=`${prefix}${targetpath}`;
            handler({obj,target,targetPath:targetpath});
        },isBubble);
    }
}


/**
 * 管道函数 输入参数返回新参数
 */
export type PipeFunction=(pars:ChangeObject)=>ChangeObject;
export class Pipe
{
    /**
     * Pipe，用于将一个Change消息按管道依次流动 表现为 func3(func2(func1(changeinfo)))
     */
    constructor()
    {
    }
    /**
     * 外部调用的通知函数 用于将一个Changed消息放入Pipe中
     * @param obj 更改对象
     * @param target 目标更改
     * @param targetPath 更改路径
     */
    public Changed(pars:ChangeObject)
    {
        let nowpars:ChangeObject=pars;
        for(let a of this.middlewares)
        {
            nowpars=a(nowpars);
        }
    }
    protected middlewares=new Array<PipeFunction>();
    public AddMiddleware(middleware:PipeFunction)
    {
        this.middlewares.push(middleware);
    }
}
/**
 * 集线器 集中调用一组监视器
 */
export class Hub
{
    constructor(){}
    protected funcs=new Set<WatchHandler>();
    public get Handlers(){return this.funcs;}
    public addFunc(func:WatchHandler)
    {
        this.funcs.add(func);
    }
    public Changed(pars:ChangeObject)
    {
        Array.from(this.funcs.values()).forEach((value)=>value(pars));
    }
}

/**
 * 同步容器 使用方法 let sync=new SyncContainer();let w1=sync.sync(obj);
 * 单纯的同步容器 ，仅用于同步对象 无法使用handle进行监视
 */
export class SyncContainer
{
    protected innerWatcher:Watcher;
    /**
     * 由内部watcher触发 change消息
     * @param pars 更改信息
     */
    protected syncHandler(pars:ChangeObject){
        let {obj,target,targetPath}=pars;
        let {top,nowpath}=getTopAndPath(obj);
        let dpath=concatPath(nowpath,targetPath);
        this.syncobjs.forEach((value)=>{
            if(value!=top)
            {
                //同步
                setProp(value,dpath,target.new,true,true,true);
            }
        });
    };
    constructor(){
        this.innerWatcher=new Watcher((pars)=>this.syncHandler(pars),false);
    }
    protected syncobjs=new Set<Object>();
    //raw => proxy
    protected raw_proxy=new Map<Object,Object>();
    public get SyncObjects():Readonly<Array<Object>>
    {
        return Array.from(this.syncobjs.values());
    }
    public get Data()
    {
        let res=this.syncobjs.values().next();
        if(!res.done) return res.value;
        return null;
    }
    public sync<T extends Object>(obj:T):T
    {
        if(this.raw_proxy.has(obj)) return this.raw_proxy.get(obj) as T;
        let proxy=this.innerWatcher.toProxiedData(obj);;
        this.syncobjs.add(proxy);
        this.raw_proxy.set(obj,proxy);
        return proxy;
    }
}


export type OuterChangeHandler=(changeinfo:ChangeInfo,path:string)=>void;
/**
 * 开放同步容器
 * 提供一个DataChanged函数供外部调用
 * 并在内部数据更改时通过 handler向外部通知
 */
export class OpenSyncContainer extends SyncContainer
{
    constructor(public OuterHandler:OuterChangeHandler)
    {
        super();
    }
    /**
     * 外部调用的数据更改函数
     * @param changeinfo 更改信息
     * @param path 总路径 即更改位置相对于top object的路径
     */
    public DataChanged(changeinfo:ChangeInfo,path:string)
    {
        //假定路径上不存在非Object的情况（正常使用）
        this.syncobjs.forEach((value)=>{
            setProp(value,path,changeinfo.new,true,true,true);
        });
    }

    public syncHandler(pars:ChangeObject)
    {
        //原封不动复制代码 为了避免调用 getTopAndPath和concatPath
        let {obj,target,targetPath}=pars;
        let {top,nowpath}=getTopAndPath(obj);
        let dpath=concatPath(nowpath,targetPath);
        this.syncobjs.forEach((value)=>{
            if(value!=top)
            {
                //同步
                setProp(value,dpath,target.new,true,true,true);
            }
        });
        //发出消息
        this.OuterHandler(pars.target,dpath);
    }
}
export type TransFromToFunc=(changeinfo:ChangeInfo,path:string)=>any;
export type TransFromFromFunc=(info:any)=>{changeinfo:ChangeInfo,path:string};
/**
 * 抽象同步器 接收一个OpenSyncContainer
 * 和OpenSyncContainer一样 通过DataChanged传入 OuterHandler传出 
 */
export class TransFormer
{
    constructor(public container:OpenSyncContainer,
                public transform_to_func:TransFromToFunc,
                public transform_from_func:TransFromFromFunc,
                public OuterHandler:(info:any)=>void)
    {
        this.container.OuterHandler=(changeinfo:ChangeInfo,path:string)=>this.Send(changeinfo,path);
    }
    public DataChanged(info:any)
    {
        let {changeinfo,path}=this.transform_from_func(info);
        this.container.DataChanged(changeinfo,path);
    }
    protected Send(changeinfo:ChangeInfo,path:string)
    {
        let info=this.transform_to_func(changeinfo,path);
        this.OuterHandler(info);
    }
}
export abstract class BaseTransFrom extends TransFormer
{
    constructor(container:OpenSyncContainer,
                OuterHandler:(info:any)=>void)
    {
        super(container,
                (a,b)=>this.TransFrom_To(a,b),
                (info)=>this.TransFrom_From(info),
                OuterHandler);
    }
    public abstract TransFrom_To(changeinfo:ChangeInfo,path:string):any;
    public abstract TransFrom_From(info:any):{changeinfo:ChangeInfo,path:string};

}
//通过对象传递
export class ObjectTransForm extends BaseTransFrom
{
    public TransFrom_To(changeinfo: ChangeInfo, path: string) {
        return {changeinfo,path};
    }
    public TransFrom_From(info: any): { changeinfo: ChangeInfo; path: string; } {
        return info;
    }
    constructor(container:OpenSyncContainer,OuterHandler:(info:string)=>void)
    {
        super(container,OuterHandler);
    }
}
export class JSONTransFrom extends BaseTransFrom
{
    public TransFrom_To(changeinfo: ChangeInfo, path: string) {
        let obj={changeinfo,path};
        let str=JSON.stringify(obj);
        return str;
    }
    public TransFrom_From(info: string): { changeinfo: ChangeInfo; path: string; } {
        let obj=JSON.parse(info);
        return obj;
    }
    constructor(container:OpenSyncContainer,OuterHandler:(info:string)=>void)
    {
        super(container,OuterHandler);
    }
}

//deliver
//source决定了 其接收什么类型数据 其发送什么类型数据
//deliver 在接收到数据时 调用source的DataChanged 在source发送数据时起传递作用
export  abstract class BaseDeliver
{
    public abstract DeliverTo(info:any);
    public OnReceive(data:any)
    {
        this.source.DataChanged(data);
    }
    constructor(public source:TransFormer)
    {
        source.OuterHandler=(info)=>{this.DeliverTo(info)};
    }
}

//worker相关
//worker内使用的同步器
export class WorkerDeliver extends BaseDeliver
{
    public DeliverTo(info: any) {
        postMessage(info,this.name);
    }

    constructor(source:TransFormer,public name:string)
    {
        super(source);
        //监听
        onmessage=(ev)=>{
            this.OnReceive(ev.data);
        }
    }
}
//client server相关
export class ClientDeliver extends BaseDeliver
{
    public async DeliverTo(info: any) {
        let res=await fetch(this.SendUrl,{
            method:"Post",
            body:info
        });
        if(!res.ok) throw {msg:"更改发送失败",info};
    }

    public get ReceiverObject():Readonly<EventSource>
    {
        return this.Receiver;
    }
    public set ReceiverObject(ev:Readonly<EventSource>)
    {
        this.Receiver.onmessage=()=>{};
        this.Receiver=ev;
        this.Receiver.onmessage=(e)=>{
            this.OnReceive(e.data);
        };
    }
    constructor(source: TransFormer,public SendUrl:string,protected Receiver:EventSource)
    {
        super(source);
        //这里添加SSE监听
        this.ReceiverObject=Receiver;
    }

}

let buffer:any=null;
let tests=new Set<TestDeliver>();
function send(sender:TestDeliver)
{
    tests.forEach((t)=>{
        if(sender!=t)
            t.OnReceive(buffer);
    });
}

export class TestDeliver extends BaseDeliver
{
    public DeliverTo(info: any) {
        buffer=info;
        send(this);
    }
    constructor(source:TransFormer)
    {
        super(source);
    }
}
/**
 * 数据监视器 工具类 默认冒泡 以后添加非冒泡选项
 * 使用方法 DataWatcher.watch(handler,obj,prefix="")
 * 或 DataWatcher.from(handler).prefix(prefix).watch(obj)
 */
export class DataWatcher
{
    public static  from(handler:WatchHandler){
        return {prefix(prefix:string){
            return {watch(obj:any){
                return new PrefixWatcher(handler,prefix).toProxiedData(obj);
            }}
        }}
    }
    public static watch(handler:WatchHandler,obj:any,prefix:string="")
    {
        return DataWatcher.from(handler).prefix(prefix).watch(obj);
    }
}

//工具函数
function dirname(path:string)
{
    let ret=path.split("/").slice(0,-1).join("/")
    if(ret=="") ret="/";
    return ret;
}
function objname(path:string)
{
    // /=>""
    let arr= path.split("/");
    return arr[arr.length-1];
}
function concatPath(path1:string,path2:string)
{
    if(path1=="/") return path2;
    if(path2=="/") return path1;
    return path1+path2;
}
/**
 * 存储器类
 * 提供注册要监视对象的功能
 * 可以自定义监视的路径 对同一个路径可添加多个监视函数 
 */
export class Store
{
    public get Data()
    {
        return this.storeObject;
    }
    /**
     * 总存储对象
     */
    protected storeObject={};
    /**
     * storeHandler
     * 总handler
     */
    protected storeHandler=(pars:ChangeObject)=>{
        //分发消息

        //如果obj这个对象的path对应的监听器存在
        let objpath=getPath(pars.obj);
        if(this.existsPath(objpath))
        {
            let arr=this.listenerMap.get(objpath);
            arr&&(Array.from(arr.values()).map((watcher)=>{
                watcher(pars);
            }));
        }
    }
    //store的监视器对象
    public watcher:Watcher;
    constructor(initStoreObject:any){
        this.watcher=new Watcher(this.storeHandler);
        this.storeObject=this.watcher.toProxiedData({});

    }
    //path=>listenerlist
    public listenerMap=new Map<string,Set<WatchHandler>>();
    public existsPath(path:string)
    {
        return this.listenerMap.has(path);
    }
    public ensuerExists(path:string)
    {
        if(!this.existsPath(path)) this.listenerMap.set(path,new Set());
    }
    /**
     * 对一个路径注册一组监听器
     * @param path 注册的路径
     * @param listener 监听器
     */
    public registerListener(path:string,listener:WatchHandler)
    {
        this.ensuerExists(path);
        let arr=this.listenerMap.get(path);
        arr&&(arr.add(listener));
    }
    /**
     * 
     * @param path 从
     * @param listener 
     */
    public removeListener(path:string,listener:WatchHandler)
    {
        if(this.existsPath(path)) 
        {
            let arr=this.listenerMap.get(path);
            arr&&(arr.delete(listener));
        }
    }
    //辅助函数
    public removeListeners=(path:string,...listeners:WatchHandler[])=>listeners.map((listener)=>this.removeListener(path,listener));
    public registerListeners=(path:string,...listeners:WatchHandler[])=>listeners.map((listener)=>this.registerListener(path,listener));
    /**
     * 存储一个对象(返回这个对象的Proxied对象)或存储一个值 返回值本身
     * @param obj 要store的对象
     * @param path path(/a/b) or propname(abc)
     */
    public store(obj:any,path:string)
    {
        if(path.trim()[0]!='/')
        {
            path=`/${path}`;
        }
        //由于已经是proxied对象所以设置NoProxy为true
        ensuerPathExists(this.Data,path);
        let parent=getProp(this.Data,dirname(path));
        let ret=this.watcher.toProxiedData(obj,parent,objname(path));
        return ret;

    }
}


let p={
    test:{
        hello:{
            why:"hhhh"
        },
        aaa:{
            bbb:{
                a:{
                    bbb:1111,
                    aaaa:1111
                }
            }
        }
    }
};
let store=new Store({});
p=store.store(p,"hello");
store.registerListeners("/hello",(obj)=>{
    console.log(obj.targetPath);
},(obj)=>{
    console.log("ehllo world");
},(obj)=>{
    let b=obj.target;
    console.log(`更改 ${b.old}=>${b.new}`);
});
function getTestSyncer():SyncContainer
{
    let sync=new OpenSyncContainer(()=>{});
    let trans=new JSONTransFrom(sync,()=>{});
    let deliver=new TestDeliver(trans);
    tests.add(deliver);
    return sync;
}

let sync=getTestSyncer();

let sync2=getTestSyncer();
let obj1=sync.sync({});
let obj2=sync2.sync({});
obj1["test"] = 111;
obj2["test2"] = {};
obj2["test2"]["hello"] = {};
obj2["test2"]["hello"]['ttt']=1;
let tobj=sync.sync(document.body.style);
let hobj=sync.sync(document.getElementsByTagName("div")[0]);
hobj.innerHTML="hello world";
window["hobj"]=hobj;
let sobj=sync.sync({});
tobj.background="red";
console.log(obj1,obj2);
function buttonClick()
{
    console.time("100000次同步耗时");
    for(let i=0;i<100000;++i){
        obj2["test2"]["hello"]['ttt']++;
    }
    console.timeLog("100000次同步耗时",obj1,obj2);
    console.timeEnd("100000次同步耗时");
}
let btn=document.getElementById("test");
btn&&(btn.onclick=buttonClick);